local component = require("component")
local event = require("event")
local fs = require("filesystem")
local computer = require("computer")

-- 配置区
local WAIT_INTERVAL = 3
local OPENOS_FILES = {
    "/init.lua",
    "/bin/ls.lua"
}

-- 不应被视为硬盘的挂载点
local EXCLUDED_MOUNTS = {
    "/tmp", "/dev", "/proc", "/sys"
}

-- 主程序日志
local logFile = "/home/disk_cloner.log"
local function logMessage(message)
    print(message)
    --local logData = os.date("[%Y-%m-%d %H:%M:%S] ") .. message .. "\n"
    --if not fs.exists("/home") then
    --    fs.makeDirectory("/home")
    --end
    --local logHandle = io.open(logFile, "a")
    --if logHandle then
    --    logHandle:write(logData)
    --    logHandle:close()
    --end
end


-- 检查挂载点是否为特殊文件系统
local function isSpecialMount(mountPath)
    if not mountPath then
        return false
    end

    for _, excludePath in ipairs(EXCLUDED_MOUNTS) do
        if mountPath == excludePath then
            return true
        end
    end
    return false
end

-- 检查文件系统是否可写（仅用于空盘判断）
local function isWritable(mountPath)
    if not mountPath then
        return false
    end

    local testFile = mountPath .. "/.write_test_" .. math.random(1000, 9999)
    local canWrite = false

    local success = pcall(function()
        local handle = io.open(testFile, "w")
        if handle then
            handle:write("test")
            handle:close()
            fs.remove(testFile)
            canWrite = true
        end
    end)

    return canWrite
end

-- 扫描所有连接的磁盘
local function scanDisks()
    local disks = {}
    local systemAddress = nil

    -- 获取系统盘地址
    for proxy, path in fs.mounts() do
        if path == "/" then
            systemAddress = proxy.address
            break
        end
    end

    -- 处理每个文件系统组件（跳过系统盘）
    for address, name in component.list("filesystem", true) do
        if address ~= systemAddress then
            local filesys = component.proxy(address)
            if filesys then
                local isOpenOS = false
                local isEmpty = false
                local mountPath = nil
                local tempMounted = false

                -- 检查是否已挂载
                for proxy, path in fs.mounts() do
                    if proxy.address == address then
                        mountPath = path
                        break
                    end
                end

                -- 跳过特殊挂载点（tmpfs、devfs等）
                if isSpecialMount(mountPath) then
                    goto continue
                end

                -- 如果没有挂载，尝试临时挂载到/mnt目录进行检查
                if not mountPath then
                    local tempPath = "/mnt/" .. address:sub(1, 8)
                    if not fs.exists(tempPath) then
                        fs.makeDirectory(tempPath)
                    end

                    local success = pcall(fs.mount, filesys, tempPath)
                    if success then
                        mountPath = tempPath
                        tempMounted = true
                    end
                end

                if mountPath then
                    -- 先检查是否为OpenOS盘（不需要可写性检查）
                    isOpenOS = true
                    for _, file in ipairs(OPENOS_FILES) do
                        if not fs.exists(mountPath .. file) then
                            isOpenOS = false
                            break
                        end
                    end

                    -- 如果不是OpenOS盘，再检查是否为空盘（需要可写性检查）
                    if not isOpenOS then
                        -- 先检查是否为空
                        local hasFiles = (fs.list(mountPath)() ~= nil)
                        if not hasFiles then
                            -- 空目录还需要检查是否可写
                            isEmpty = isWritable(mountPath)
                        else
                            isEmpty = false
                        end
                    end

                    -- 如果是临时挂载，检查完后卸载
                    if tempMounted then
                        fs.umount(filesys)
                        mountPath = nil
                    end
                else
                    -- 无法访问的磁盘，如果能成功获取到组件，认为是空盘
                    isEmpty = true
                end

                -- 获取标签
                local label = ""
                if filesys.setLabel and filesys.getLabel then
                    label = filesys.getLabel() or ""
                end

                table.insert(disks, {
                    address = address,
                    mountPath = mountPath,
                    isOpenOS = isOpenOS,
                    isEmpty = isEmpty,
                    label = label,
                    filesys = filesys
                })
            end
        end
        ::continue::
    end

    return disks
end

-- 复制目录内容（递归）
local function copyDirectory(source, target)
    if not fs.exists(target) then
        fs.makeDirectory(target)
    end

    for file in fs.list(source) do
        local sourcePath = source .. "/" .. file
        local targetPath = target .. "/" .. file

        if fs.isDirectory(sourcePath) then
            copyDirectory(sourcePath, targetPath)
        else
            local success, err = pcall(fs.copy, sourcePath, targetPath)
            if not success then
                return false, err
            end
        end
    end
    return true
end

-- 智能复制决策和操作
local function smartClone()
    local disks = scanDisks()
    local openOSDisks = {}
    local emptyDisks = {}

    for _, disk in ipairs(disks) do
        if disk.isOpenOS then
            table.insert(openOSDisks, disk)
        elseif disk.isEmpty then
            table.insert(emptyDisks, disk)
        end
    end

    logMessage(string.format("扫描结果: OpenOS盘:%d 空盘:%d", #openOSDisks, #emptyDisks))

    if #openOSDisks == 0 then
        logMessage("系统状态：未检测到任何OpenOS源盘")
        return false, "无源盘"
    elseif #openOSDisks > 1 then
        local osLabels = {}
        for _, disk in ipairs(openOSDisks) do
            table.insert(osLabels, disk.label or disk.address:sub(1, 8))
        end
        logMessage(string.format("系统状态：检测到多个OpenOS盘 (%d个)，操作已暂停: %s",
                #openOSDisks, table.concat(osLabels, ", ")))
        return false, "多源盘冲突"
    end

    local source = openOSDisks[1]

    if #emptyDisks == 0 then
        logMessage(string.format("系统状态：检测到源盘 [%s]，但未找到空盘", source.label or source.address:sub(1, 8)))
        return false, "无目标盘"
    end

    local target = emptyDisks[1]

    -- 确保目标盘已挂载
    local targetPath = target.mountPath
    if not targetPath then
        targetPath = "/mnt/" .. target.address:sub(1, 8)
        if not fs.exists(targetPath) then
            fs.makeDirectory(targetPath)
        end
        local success = pcall(fs.mount, target.filesys, targetPath)
        if not success then
            logMessage("无法挂载目标盘")
            return false, "挂载失败"
        end
        target.mountPath = targetPath
    end

    -- 确保源盘已挂载
    local sourcePath = source.mountPath
    if not sourcePath then
        sourcePath = "/mnt/" .. source.address:sub(1, 8)
        if not fs.exists(sourcePath) then
            fs.makeDirectory(sourcePath)
        end
        local success = pcall(fs.mount, source.filesys, sourcePath)
        if not success then
            logMessage("无法挂载源盘")
            return false, "挂载失败"
        end
        source.mountPath = sourcePath
    end

    logMessage(string.format("开始复制：从 [%s] 到 [%s]",
            source.label or source.address:sub(1, 8),
            target.label or target.address:sub(1, 8)))

    -- 复制文件内容而不是目录本身
    local success, reason = copyDirectory(sourcePath, targetPath)

    if success then
        logMessage("复制成功！")

        if target.filesys and target.filesys.setLabel then
            -- 修改标签格式为完整的年月日时分秒
            local newLabel = "OpenOS_Clone_" .. os.date("%Y%m%d%H%M%S")
            target.filesys.setLabel(newLabel)
            logMessage("新标签: " .. newLabel)
        end

        return true
    else
        logMessage("复制失败: " .. (reason or "未知错误"))
        return false, reason
    end
end

-- 主程序
print("===== 智能OpenOS磁盘克隆系统 =====")
print("按 Ctrl+C 退出程序")
logMessage("系统启动 - 等待磁盘操作")

local cooldown = 0
local running = true

-- 注册中断信号处理
event.listen("interrupted", function()
    logMessage("收到中断信号，程序退出")
    running = false
end)

while running do
    if cooldown > 0 then
        cooldown = cooldown - 1
    else
        local success, reason = smartClone()
        if success then
            cooldown = 10
        elseif reason == "无源盘" or reason == "无目标盘" then
            cooldown = 0
        else
            cooldown = 5
        end
    end

    -- 使用computer.pullSignal进行定时，同时检查中断信号
    local signal = computer.pullSignal(WAIT_INTERVAL)
    if signal == "interrupted" then
        break
    end
end

logMessage("程序正常结束")
